package com.app.framework.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.app.framework.annotations.CascadeRemove;
import com.app.framework.annotations.Id;
import com.app.framework.annotations.Index;
import com.app.framework.annotations.Save;
import com.app.framework.annotations.TransferInvisible;
import com.app.framework.annotations.Transient;
import com.app.framework.exception.ORMException;
import com.app.framework.orm.util.ClassHelper;
import com.app.framework.util.helper.ClassInfo;
import com.app.framework.util.helper.FieldInfo;

public class ReflectionUtil {
	
	//private final static String MODIFY_MARK="modify_";
	private static ConcurrentHashMap<String,ClassInfo> classInfoMap=new ConcurrentHashMap<String, ClassInfo>();
	
	public static ClassInfo getClassInfo(Class<?> clz) throws ORMException {
		clz = ClassHelper.getRecordClass(clz);
		String className=clz.getName();
		if(classInfoMap.get(className) != null) {
			return classInfoMap.get(className);
		}
		ClassInfo classInfo=new ClassInfo();
		classInfo.setClz(clz);
		Class<?> surperclass=clz;
		FieldInfo fieldInfo=null;
		String fieldName=null;
		while(surperclass != Object.class) {
			Field[] fs=surperclass.getDeclaredFields();
			List<Field> lst = Arrays.asList(fs);
			Collections.reverse(lst);//反转顺序,为的建表时字段的顺序为类属性定义的顺序
			for(Field f:lst) {
				if(Modifier.isAbstract(f.getModifiers())) {
					continue;
				}
				if(Modifier.isFinal(f.getModifiers())) {
					continue;
				}
				if(Modifier.isStatic(f.getModifiers())) {
					continue;
				}
				fieldInfo=new FieldInfo();
				f.setAccessible(true);
				fieldInfo.setField(f);
				//为byRefence byValue赋值
				if(f.isAnnotationPresent(Save.class)) {
					//判断能否加reference value注解
//					if(!isReferenceable(f)) { 
//						logger.error("不合法的引用类型:"+className+","+f.getName());
//						throw new ORMException("不合法的引用属性类型:"+className+","+f.getName());
//					}
					Save save = f.getAnnotation(Save.class);
					if(save.type().equals("reference")) {
						fieldInfo.setByReference(true);
						//是否被级联删除注释
						if(f.isAnnotationPresent(CascadeRemove.class)) {
							//生成对应的get方法
							String getMethod = ClassHelper.genGetMethodNameByField(f);
							for(Method m : surperclass.getDeclaredMethods()) {
								if(m.getName().equals(getMethod)) {
									classInfo.getCascadeRemoveMethods().add(m);
									break;
								}
							}
						}
					}
					else {
						fieldInfo.setByValue(true);
					}
				}
				else {
					//为jsonableCollection赋值
					if(isJsonableCollection(f)) {
						fieldInfo.setJsonableCollection(true);
					}
					//为jsonableArray赋值
					if(isJsonableArray(f)) {
						fieldInfo.setJsonableArray(true);
					}
				}
				
				fieldName=f.getName();
				if(!f.isAnnotationPresent(TransferInvisible.class)) {
					if(!classInfo.getTransferFields().containsKey(fieldName)) {
						classInfo.getTransferFields().put(fieldName, fieldInfo);
					}
				}
				if(!f.isAnnotationPresent(Transient.class)) {
					if(!classInfo.getPersistFields().containsKey(fieldName)) {
						classInfo.getPersistFields().put(fieldName, fieldInfo);
					}
				}
				if(f.isAnnotationPresent(Id.class)) {
					if(classInfo.getIdField() == null) {
						classInfo.setIdField(fieldInfo);
					}
					else {
						throw new ORMException("id field must be unique");
					}
					
				}
				if(f.isAnnotationPresent(Index.class)) {
					ClassHelper.checkIndexType(f.getType());
					if(!classInfo.getIndexFields().containsKey(fieldName)) {
						classInfo.getIndexFields().put(fieldName, fieldInfo);
					}
				}
			}
			surperclass=surperclass.getSuperclass();
		}
		ClassInfo tmp = classInfoMap.putIfAbsent(className, classInfo);
		if(tmp != null) {
			classInfo = tmp;
		}
		return classInfo;
	}
	
	/**
	 * 是否可加reference 或者value注解
	 * @param fieldType
	 * @return
	 */
//	private static boolean isReferenceable(Field field) {
//		
//		if(ClassHelper.isList(field.getType())) {
//			return isReferenceableList(field);
//		}
//		if(ClassHelper.isMap(field.getType())) {
//			return isReferenceableMap(field);
//		}
//		if(ClassHelper.isRecord(field.getType())) {
//			return true;
//		}
//		return false;
//	}
	
	/**
	 * 集合中泛型规定的数据如果是简单类型可以直接转json
	 * @param field
	 * @return
	 */
	private static boolean isJsonableCollection(Field field) {
		Class<?> type = field.getType();
		if(ClassHelper.isList(type)) {
			return isJsonableList(field);
		}
		if(ClassHelper.isMap(type)) {
			return isJsonableMap(field);
		}
		return false;
	}
	
	private static boolean isJsonableArray(Field field) {
		String name = field.getType().getName();
		if(!field.getType().isArray()) {
			return false;
		}
		if (name.indexOf("[Ljava.lang.String") != -1
				|| name.indexOf("[Ljava.lang.Integer") != -1|| name.indexOf("[I") != -1 
				|| name.indexOf("[Ljava.lang.Long") != -1 || name.indexOf("[J") != -1
				|| name.indexOf("[Ljava.lang.Double") != -1|| name.indexOf("[D") != -1
				|| name.indexOf("[Ljava.lang.Float") != -1 || name.indexOf("[F") != -1
				|| name.indexOf("[Ljava.lang.Short") != -1|| name.indexOf("[S") != -1
				|| name.indexOf("[Ljava.lang.Boolean") != -1 || name.indexOf("[Z") != -1
				|| name.indexOf("[Ljava.lang.Character") != -1 || name.indexOf("[Z") != -1
				|| name.indexOf("[Ljava.lang.Byte") != -1 || name.indexOf("[B") != -1
				) {
			return true;
		}
		return false;
	}
	
//	private static boolean isReferenceableList(Field field) {
//		try {
//			Class<?> clz = (Class<?>)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0];
//			return ClassHelper.isRecord(clz);
//		} catch (Exception e) {
//			return false;
//		}
//	}
//	
//	private static boolean isReferenceableMap(Field field) {
//		try {
//			Type[] tmp = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
//			Type tmp1 = tmp[1];
//			tmp1.toString();
//			return ClassHelper.isSimpleClass((Class<?>)tmp[0]) && (ClassHelper.isRecord((Class<?>)tmp[1]));
//		}catch (Exception e) {
//			return false;
//		}
//	}
	private static boolean isJsonableList(Field field) {
		try {
			return ClassHelper.isSimpleClass((Class<?>)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]);
		} catch (Exception e) {
			return false;
		}
	}
	
	/**
	 * 允许 Map<String,List<String>> map 这种类型可json化
	 * @param field
	 * @return
	 */
	private static boolean isJsonableMap(Field field) {
		try {
			Type[] tmp = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
			if(tmp[1] instanceof ParameterizedType) {
				ParameterizedType pt = (ParameterizedType)tmp[1];
				if(ClassHelper.isList((Class<?>) pt.getRawType())) {
				return ClassHelper.isSimpleClass((Class<?>)tmp[0]) && ClassHelper.isSimpleClass((Class<?>)pt.getActualTypeArguments()[0]);
				}
				else {
					return false;
				}
			}
			return ClassHelper.isSimpleClass((Class<?>)tmp[0]) && (ClassHelper.isSimpleClass((Class<?>)tmp[1]));
		}catch (Exception e) {
			return false;
		}
	}
	
	public static Map<String,Method> getClassPublicUnStaticUnFinalMethods(Class<?> clz) {
		Map<String,Method> map = new HashMap<String,Method>();
		
		Class<?> surperclass=clz;
		
		while(surperclass != Object.class) {
			for(Method m : surperclass.getDeclaredMethods()) {
				if(Modifier.isPublic(m.getModifiers()) && !Modifier.isFinal(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())) {
					m.setAccessible(true);
					if(!map.containsKey(m.getName())) {
						map.put(m.getName(), m);
					}
				}
			}
			surperclass=surperclass.getSuperclass();
		}
		
		return map;
	}
	
	public static Map<String,Field> getClassUnStaticUnFinalFields(Class<?> clz) {
		Map<String,Field> map = new HashMap<String,Field>();
		Class<?> surperclass=clz;
		while(surperclass != Object.class) {
			for(Field f : surperclass.getDeclaredFields()) {
				if(!Modifier.isFinal(f.getModifiers()) && !Modifier.isStatic(f.getModifiers())) {
					if(!map.containsKey(f.getName())) {
						map.put(f.getName(), f);
					}
				}
			}
			surperclass=surperclass.getSuperclass();
		}
		
		return map;
	}
}
